﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FluentValidation;
using FluentValidation.Results;

namespace Revis.VClinicNet.App.Internal.Dto.Validation.Fluent
{
    public interface IValidationChecker<TDto>
    {
        void Check(TDto dto);
    }

    public interface IValidatorRuleChooser<TDto>
    {
        IValidationChecker<TDto> Using<TRule>() where TRule : ValidationRules<TDto>, new();
    }

    public abstract class ValidationRules<TDto> : AbstractValidator<TDto>
    {
        public ValidationRules()
        {
            SetUpRules();
        }

        public abstract void SetUpRules();
    }

    public class Validator<TDto> : IValidatorRuleChooser<TDto>, IValidationChecker<TDto>
    {
        private ValidationRules<TDto> _ruleValidator = null;

        public IValidationChecker<TDto> Using<TRule>() where TRule : ValidationRules<TDto>, new()
        {
            _ruleValidator = new TRule();
            return this;
        }

        public void Check(TDto dto)
        {
            ValidationResult result = _ruleValidator.Validate(dto);
            if (result.IsValid == false) {
                throw new ValidationException("Błąd walidacji danych", result);
            }
        }

    }

    public static class FluenValidationExtensions
    {
        public static IRuleBuilderOptions<T, TProperty> RequireWithMessage<T, TProperty>(
            this IRuleBuilderInitial<T, TProperty> builder, string message)
        {
            return builder.NotNull().WithMessage(message).NotEmpty().WithMessage(message);
        }
    }

}

namespace Revis.VClinicNet.App.Internal.Dto.Validation 
{
    using Revis.VClinicNet.App.Internal.Dto.Validation.Fluent;

    public static class Validation
    {
        public static IValidatorRuleChooser<TDto> For<TDto>()
        {
            return new Validator<TDto>();
        }
    }

    public class ValidationError
    {
        public string Key { get; set; }
        public string Message { get; set; }

        public ValidationError(string key, string msg)
        {
            Key = key;
            Message = msg;
        }

        public static implicit operator ValidationError(ValidationFailure failure)
        {
            return new ValidationError(failure.PropertyName, failure.ErrorMessage);
        }

        public void AddKeyPrefix(string prefix)
        {
            Key = prefix + Key;
        }
    }

    public class ValidationException : ClinicException
    {
        public List<ValidationError> Errors { get; protected set; }

        public ValidationException(string msg)
            : base(msg)
        {
            Errors = new List<ValidationError>();
        }

        public ValidationException(string msg, ValidationResult result)
            : this(msg)
        {
            Errors = result.Errors.Select(vf => (ValidationError)vf).ToList();
        }

        public void Add(ValidationError error)
        {
            Errors.Add(error);
        }

        public void AddErrorKeyPrefix(string prefix)
        {
            foreach (ValidationError ve in Errors) {
                ve.AddKeyPrefix(prefix);
            }
        }
    }
}
